class Painter {
  /**每秒画布绘制次数 */
  FPS = 60;
  /**绘制队列 */
  paintObjs = [];
  /**飞行器队列 */
  planes = [];
  /**
   * @param {HTMLCanvasElement} canvas
   */
  constructor(canvas) {
    this.canvas = canvas;
    this.pen = canvas.getContext("2d");
    var tis = this;
    window.interval = setInterval(function () {
      tis.onPainting();
    }, Math.floor(1000 / this.FPS));
  }

  /**绘制回调 */
  onPainting() {
    let pen = this.pen;
    //清除画布
    pen.clearRect(0, 0, this.canvas.width, this.canvas.height);

    //绘制所有图片对象
    for (let i = 0; i < this.paintObjs.length; i++) {
      //绘制对象
      let elm = this.paintObjs[i];
      pen.master = elm;
      //检测是否被标记释放
      if (elm.isDispose) {
        if (elm instanceof PlaneBase) {
          //删除飞行器组中的匹配对象
          for (let j = 0; j < this.planes.length; j++) {
            const p = this.planes[j];
            if (elm === p) {
              this.planes.splice(j, 1);
              break;
            }
          }
        }
        if (elm.onDispose) elm.onDispose.call(elm, elm);
        this.paintObjs.splice(i, 1);
        i--;
        continue;
      }

      //设置颜色
      pen.strokeStyle = elm.color;
      pen.fillStyle = elm.color;
      let tx = elm.x;
      let ty = elm.y;
      elm.half_w = elm.width / 2;
      elm.half_h = elm.height / 2;
      let angle = elm.angle;
      //设置画笔原点
      pen.translate(tx + elm.roOrigin.x, ty + elm.roOrigin.y);
      //设置旋转角度
      pen.rotate(angle * (Math.PI / 180));
      //触发绘制回调
      if (elm.onPainting) elm.onPainting.call(elm, pen);
      //还原角度
      pen.rotate(-angle * (Math.PI / 180));
      //还原画布原点
      pen.translate(-tx - elm.roOrigin.x, -ty - elm.roOrigin.y);
      //绘制战机开火点
      // if (elm instanceof CanvaElementByImg && elm.drawDefault) {
      //   pen.fillStyle = "red";
      //   pen.fillRect(elm.gunpoint.x, elm.gunpoint.y, 10, 10);
      // }
    }
  }

  /**
   * 添加图片对象到绘制核心到绘制队列尾部
   * @param {CanvaElement} objs 图片绘制对象
   */
  addPaintElement(...objs) {
    this.paintObjs.push(...objs);
    for (let i = 0; i < objs.length; i++) {
      if (objs[i] instanceof PlaneBase) {
        this.planes.push(objs[i]);
      }
    }
  }

  /**
   * 添加图片对象到绘制核心到绘制队列头部
   * @param {CanvaElement} objs 图片绘制对象
   */
  pressPaintElement(...objs) {
    this.paintObjs.unshift(...objs);
    for (let i = 0; i < objs.length; i++) {
      if (objs[i] instanceof PlaneBase) {
        this.planes.push(objs[i]);
      }
    }
  }
}

/**基本绘制单元 */
class CanvaElement {
  /**X坐标 */
  x = 0;
  /**Y坐标 */
  y = 0;
  /**宽度 */
  width = 0;
  /**高度 */
  height = 0;
  /**是否是应该被释放的 */
  isDispose = false;
  /**颜色 */
  color = "yellow";
  /**是否启用默认绘制 */
  drawDefault = true;
  /**偏转角度 */
  angle = 0;
  /**鼠标坐标 */
  mouseLocation = new Point(0, 0);
  /**对象圆心坐标 */
  roOrigin = new Point(0, 0);
  /**在被释放前触发 */
  onDispose = Function();
  /**在每次绘制前毕后触发 */
  onPainting = Function();
  paintListens = [];
  mousemoveListens = [];
  mousedownListens = [];
  mouseupListens = [];
  keydownListens = [];
  keyupListens = [];
  /**
   *
   * @param {HTMLCanvasElement} gameCanvas
   */
  constructor() {
    var tis = this;
    gameCanvas.addEventListener("mousedown", function (e) {
      tis.eventHandler(e, tis.mousedownListens);
    });
    gameCanvas.addEventListener("mouseup", function (e) {
      tis.eventHandler(e, tis.mouseupListens);
    });
    gameCanvas.addEventListener("mousemove", function (e) {
      tis.mouseLocation = new Point(e.offsetX, e.offsetY);
      tis.eventHandler(e, tis.mousemoveListens);
    });
    document.body.addEventListener("keyup", function (e) {
      tis.eventHandler(e, tis.keyupListens);
    });
    document.body.addEventListener("keydown", function (e) {
      tis.eventHandler(e, tis.keydownListens);
    });
    this.onPainting = function (pen) {
      tis.eventHandler(pen, tis.paintListens);
    };
  }

  /**标记释放,将在下次界面刷新时释放 */
  dispose() {
    this.isDispose = true;
  }

  /**检测元素坐标矩阵是否包含某个坐标
   * @param {number} x 坐标x
   * @param {number} y 坐标y
   * @param {number} inc 扩大范围的增量
   */
  containPoint(x, y, inc = 0) {
    return this.x - inc <= x && this.x + this.width + inc >= x && this.y - inc <= y && this.y + this.height + inc >= y;
  }
  /**检测元素坐标矩阵是否包含某个X
   * @param {number} x 坐标x
   * @param {number} inc 扩大范围的增量
   */
  containPoint_X(x, inc) {
    return this.x - inc <= x && this.x + this.width + inc >= x;
  }
  /**检测元素坐标矩阵是否包含某个Y
   * @param {number} y 坐标y
   * @param {number} inc 扩大范围的增量
   */
  containPoint_Y(y, inc) {
    return this.y - inc <= y && this.y + this.height + inc >= y;
  }

  /**
   * 添加事件,如果对象被释放,则回调一并清除
   * @param {"paint"|"mousemove"|"mousedown"|"mouseup"|"keydown"|"keyup"} event 事件名称
   * @param {(any)=>void} callback 事件回调
   */
  addEventListener(event, callback) {
    this[event + "Listens"].push(callback);
  }

  /**
   * 从回调列表中删除
   * @param {"paint"|"mousemove"|"mousedown"|"mouseup"|"keydown"|"keyup"} event 事件名称
   * @param {Function} func 要删除的回调
   */
  removeEventListener(event, func) {
    this[event + "Listens"].remove(func);
  }

  //事件发送器
  eventHandler(e, callbacks) {
    for (let i = 0; i < callbacks.length; i++) {
      if (this.isDispose) {
        callbacks.splice(i, 1);
        i--;
        continue;
      }
      callbacks[i].call(this, e);
    }
  }
}

/**
 * 绘制矩形
 * @param {CanvasRenderingContext2D} pen
 */
CanvaElement.prototype.fillRect = function (pen) {
  pen.fillRect(-this.roOrigin.x, -this.roOrigin.y, this.width, this.height);
};

/**
 * 绘制圆形
 * @param {CanvasRenderingContext2D} pen
 */
CanvaElement.prototype.fillCircle = function (pen) {
  pen.fillCircle(-this.roOrigin.x, -this.roOrigin.y, this.width);
};

/**
 * 绘制三角形
 * @param {CanvasRenderingContext2D} pen
 */
CanvaElement.prototype.fillTriangle = function (pen) {
  pen.beginPath();
  pen.moveTo(this.half_w - this.roOrigin.x, 0 - this.roOrigin.y);
  pen.lineTo(-this.roOrigin.x, this.height - this.roOrigin.y);
  pen.lineTo(this.width - this.roOrigin.x, this.height - this.roOrigin.y);
  pen.fill();
  pen.closePath();
};

/**图像类 */
class CanvaElementByFrames extends CanvaElement {
  /**当前帧索引 */
  nowFrameIndex = 0;
  /**执行帧队列 */
  runframes = [];
  /**固定帧队列 */
  originalframes = [];
  /**帧切换时间间隔(ms) */
  alternateTimeSpan = 150;
  /**距离上次变帧已过去的时间 */
  alternateElapsed = 0;
  /**标识是否正在执行特殊帧 */
  isPlayingSpecial = false;
  /**在执行特殊帧时期,临时的帧切换间隔时间 */
  tempAlternateTimeSpan = 150;
  /**帧切换时触发 */
  onFrameChange = new Function();

  get Width() {
    return this.width;
  }
  set Width(value) {
    this.width = value;
    if (this.img) this.img.width = value;
  }

  get Height() {
    return this.height;
  }
  set Height(value) {
    this.height = value;
    if (this.img) this.img.height = value;
  }

  constructor() {
    super();
    this.color = "transparent";
    this.addEventListener("paint", this.doPaint);
  }

  /**
   * 设置循环帧列
   * @param {Image[]} imgs 帧序列
   */
  setFrames(imgs) {
    for (let i = 0; i < imgs.length; i++) {
      const img = imgs[i];
      img.width = this.width;
      img.height = this.height;
    }
    this.nowFrameIndex = 0;
    this.runframes = imgs;
    this.originalframes = imgs;
  }

  /** 播放指定次数的特殊帧列
   * @param {Image[]} frames 帧序列
   * @param {number} count 播放次数
   * @param {()=>void} callback 播放完毕后的回调
   */
  playFramesByCount(frames, count, callback) {
    var arr = [];
    this.isPlayingSpecial = true;
    //插入特殊帧
    for (let i = 0; i < count; i++) {
      arr.push(...frames);
    }
    this.runframes = arr;
    this.nowFrameIndex = 0;
    //在每次帧切换后删除首部特殊帧并检测
    var pcbk = function () {
      this.runframes.splice(0, 1);
      this.nowFrameIndex--;
      if (this.runframes.length <= 0) {
        this.isPlayingSpecial = false;
        this.runframes = this.originalframes;
        this.nowFrameIndex = 0;
        callback.call(this);
        this.onFrameChange = new Function();
      }
    };
    this.onFrameChange = pcbk;
  }

  /**
   * 播放指定时长的特殊帧列
   * @param {Image[]} frames 帧序列
   * @param {number} time 播放持续时长
   * @param {number} temptsp 特殊帧帧间隔
   * @param {()=>void} callback 播放完毕后的回调
   */
  playFramesByTime(frames, time, callback) {
    this.isPlayingSpecial = true;
    this.runframes = frames;
    this.nowFrameIndex = 0;
    var tis = this;
    setTimeout(function () {
      this.isPlayingSpecial = false;
      tis.runframes = tis.originalframes;
      tis.nowFrameIndex = 0;
      callback.call(tis);
    }, time);
  }

  doPaint(pen) {
    this.drawFrame(pen);
    /**只有当画面数大于0,未启用只播放首帧才切换主帧 */
    if (this.runframes.length > 0) {
      //距离上一帧的时间达到后才切换帧
      if (this.alternateElapsed >= (this.isPlayingSpecial ? this.tempAlternateTimeSpan : this.alternateTimeSpan)) {
        this.alternateElapsed = 0;
        let netindex = this.nowFrameIndex + 1;
        if (netindex >= this.runframes.length) netindex = 0;
        this.nowFrameIndex = netindex;

        if (this.onFrameChange) this.onFrameChange();
      } else {
        this.alternateElapsed += 1000 / painter.FPS;
      }
    }
  }
}

/**
 * 绘制帧
 * @param {CanvasRenderingContext2D} pen
 */
CanvaElementByFrames.prototype.drawFrame = function (pen) {
  if (this.runframes.length > 0) {
    pen.drawImage(this.runframes[this.nowFrameIndex], -this.roOrigin.x, -this.roOrigin.y, this.width, this.height);
  }
};

/**基本飞行器对象 */
class PlaneBase extends CanvaElementByFrames {
  /**战机速度*/
  speed = 5;
  /**攻击力 */
  aggr = 10;
  /**生命值 */
  life = 100;
  /**绝对枪口坐标 */
  gunpointAbs = new Point();
  /**相对枪口坐标 */
  gunpointRel = new Point();
  /**相对枪口坐标 */
  /**分组集合*/
  groupName = "empty";
  /**被敌方子弹命中的回调 (this:PlaneBase foe:命中我方的敌机对象 bull:关联的子弹) */
  onBehit = new Function();
  /**子弹命中敌方的回调 (this:PlaneBase foe:我方命中的敌机对象 bull:关联的子弹) */
  onHitTarget = new Function();
  /**释放完毕后的回调队列 */
  disposings = [];

  constructor() {
    super();
    this.Width = 50;
    this.Height = 70;
    this.addEventListener("paint", this.planeBase_dopaint);
    this.roOrigin = new Point(this.width / 2, this.height / 2);
  }

  planeBase_dopaint() {}

  /**计算枪口坐标 */
  calcGunPoint() {
    //计算枪口绝对坐标
    let gp = new Point(this.x + this.gunpointRel.x, this.y + this.gunpointRel.y);
    //计算圆心绝对坐标
    let o = new Point(this.x + this.width / 2, this.y + this.height / 2);
    let ngp = Tools.calcRolCoor(o, gp, this.angle);
    this.gunpointAbs = ngp;
  }

  /**开火(使用默认子弹)
   * @returns {YBullet}
   */
  fire() {
    this.calcGunPoint();
    var bull = new YBullet(this);
    painter.addPaintElement(bull);
    return bull;
  }

  /**
   * 开火(使用定向子弹)
   * @param {Point} end
   * @param {boolean} ext 是否沿着路线
   * @param {"rect"|"circle"|"triangle"} style 子弹样式
   * @returns {LocationBullet}
   */
  fireAt(end, ext) {
    this.calcGunPoint();
    var bull = new LocationBullet(this, end);
    bull.ext = ext;
    painter.pressPaintElement(bull);
    return bull;
  }

  /**
   * 战机重载释放,释放后在相同位置显示爆炸效果
   */
  dispose() {
    super.dispose();
    for (let i = 0; i < this.disposings.length; i++) {
      this.disposings[i].call(this);
    }
    //根据敌我类型显示不同爆炸效果
    var frams = this instanceof Foe ? resoures.anim_foeExplode() : resoures.anim_heroExplode();
    //实例爆炸对象
    var boom = new CanvaElementByFrames();
    boom.x = this.x - 20;
    boom.y = this.y - 20;
    let max = Math.max(this.width, this.height);
    boom.width = max + 30;
    boom.height = max + 30;
    boom.tempAlternateTimeSpan = 60;
    painter.addPaintElement(boom);
    boom.playFramesByCount(frams, 1, function () {
      //释放掉爆炸效果,致此战机对象完全释放
      this.dispose();
    });
  }

  /**
   * 添加事件,如果对象被释放,则回调一并清除
   * @param {"disposing"|"paint"|"mousemove"|"mousedown"|"mouseup"|"keydown"|"keyup"} event 事件名称
   * @param {(any)=>void} callback 事件回调
   */
  addEventListener(event, callback) {
    if (event == "disposing") {
      this.disposings.push(callback);
    } else {
      super.addEventListener(event, callback);
    }
  }

  /**
   * 添加事件,如果对象被释放,则回调一并清除
   * @param {"disposing"|"paint"|"mousemove"|"mousedown"|"mouseup"|"keydown"|"keyup"} event 事件名称
   * @param {Function} func 要删除的回调
   */
  removeEventListener(event, func) {
    if (event == "disposing") {
      this.disposings.remove(func);
    } else {
      super.removeEventListener(event, func);
    }
  }
}

/**敌方战舰类 */
class Foe extends PlaneBase {
  lastX = 0;
  lastY = 0;
  /**击杀分数 */
  killSoure = 0;
  /**待执行的移动路径 */
  movePoints = [];
  /**原始坐标*/
  originalLocation = new Point();
  /**移动路径角度调整间隔*/
  angleAdjustSpace = 10;
  /**当前折返方向 */
  turnBackDir = "left";
  /**折返移动间隔 */
  turnBackTimeSpan = 100;
  /**距离上次折返移动已经过去的时间 */
  turnbBackElapsed = 0;
  /**启用折返 */
  openTurnBack = false;
  /**是否已经撞到过英雄 */
  hasCollided = false;
  /**移动时间间隔(ms) */
  moveTimeSpan = 1000 / painter.FPS;
  /**距离上次移动过去的时间 */
  lastMoveElapsed = 0;
  /**与英雄碰撞时的回调(this:Foe,hero:Hero) */
  onCollision = new Function();
  /**攻击第一阶段结束时的回调,可接管接下来的攻击动作 */
  attackStageOneOver = new Function();
  /**移动完成时的回调 */
  onMoveComplete = new Function();
  /**即将攻击前的回调*/
  beginAttack = new Function();
  /**指示单位是否正在进行坐标路径移动 */
  isMoving = false;

  constructor() {
    super();
    this.groupName = "galaga";
    this.width = 30;
    this.height = 30;
    this.gunpointRel = new Point(this.width / 2, 0);
    this.roOrigin = new Point(this.width / 2, this.height / 2);
    this.addEventListener("paint", this.foe_dopaint);
    this.onBehit = this.onBhit;
    this.onCollision = this.collision;
    this.speed = 1;
  }

  //绘制回调
  foe_dopaint(pen) {
    //左右折返移动
    if (this.movePoints.length == 0 && this.openTurnBack) {
      if (this.turnbBackElapsed >= this.moveTimeSpan) {
        this.turnbBackElapsed = 0;
        if (this.turnBackDir == "left") {
          this.x -= this.speed;
        } else {
          this.x += this.speed;
        }
        if (this.x <= this.originalLocation.x - 50) this.turnBackDir = "right";
        if (this.x >= this.originalLocation.x + 50) this.turnBackDir = "left";
      } else {
        this.turnbBackElapsed += 1000 / painter.FPS;
      }
    }
    //如果移动坐标集中长度大于0则移动到首元素坐标,并根据条件调整朝向角度
    if (this.movePoints.length > 0) {
      this.isMoving = true;
      if (this.lastMoveElapsed >= this.moveTimeSpan) {
        this.lastMoveElapsed = 0;
        var pt = this.movePoints.splice(0, 1)[0];
        this.x = pt.x;
        this.y = pt.y;
        //到达间隔开始调整角度
        if (this.movePoints.length % this.angleAdjustSpace == 0) {
          this.adjustAngle(this.angleAdjustSpace);
        }
        if (this.movePoints.length == 0) {
          this.angle = 0;
          this.onMoveComplete();
        }
      } else {
        this.lastMoveElapsed += 1000 / painter.FPS;
      }
    }
    //检测是否碰撞到英雄战机
    if (!this.hasCollided) {
      if (this.x != this.lastX || this.y != this.lastY) {
        this.lastX = this.x;
        this.lastY = this.y;
        for (let i = 0; i < heros.length; i++) {
          let hero = heros[i];
          //检测战机
          if (hero.containPoint(this.x, this.y) || hero.containPoint(this.x + this.width, this.y) || hero.containPoint(this.x, this.y + this.height) || hero.containPoint(this.x + this.width, this.y + this.height)) {
            this.hasCollided = true;
            hero.onCollision(this); //触发英雄方回调
            this.onCollision(hero); //触发我方回调
            break;
          }
        }
      }
    }
  }

  /**立即根据坐标调整朝向
   * @param {number} next 离下一坐标的索引间距
   */
  adjustAngle(next) {
    if (this.movePoints.length == 0) return;
    //当前位置
    var start = this.movePoints[0];
    var end;
    //得出下一位置
    if (this.movePoints.length > next) {
      end = this.movePoints[next];
    } else {
      end = this.movePoints[this.movePoints.length - 1];
    }
    //计算并设置角度
    var ang = Tools.calcAngle(start.x, start.y, end.x, end.y);
    this.angle = ang;
  }

  //与英雄碰撞后的回调
  collision(hero) {
    this.dispose();
  }

  //被子弹命中的回调
  onBhit(foe, bull) {
    //减去生命值
    this.life -= bull.aggr;
    if (this.life <= 0) {
      this.dispose();
    }
  }

  /**
   * 设置移动路径
   * @param {Point[]} points
   */
  setMovePath(points) {
    this.movePoints = points;
    this.x = points[0].x;
    this.y = points[0].y;
    this.adjustAngle(1);
  }

  /**
   * 设置移动目标
   * @param {Point} 目标坐标
   */
  setMoveTarget(end) {
    var points = Tools.calcLinePoints(this.x, this.y, end.x, end.y);
    this.movePoints = points;
    this.x = points[0].x;
    this.y = points[0].y;
    this.adjustAngle(1);
  }

  /**攻击英雄 */
  attack() {
    if (this.isDispose) return;
    this.beginAttack();
    this.fireBull();
    let lef = this.originalLocation.x <= cavW / 2;
    let lc1 = new Point(0, this.y - 50);
    let lc2 = new Point(0, cavH / 2 - 50);
    let rc1 = new Point(cavW, this.y - 50);
    let rc2 = new Point(cavW, cavH / 2 - 50);
    //贝塞尔阶段,蜜蜂进入玩家上方攻击区任意x坐标点
    let end = {
      x: lef ? Tools.getRandInt(0, cavW / 2) : Tools.getRandInt(cavW / 2, cavW - this.width),
      y: cavH / 2 + 50,
    };
    var path = Tools.calcThreeBezierPoints(new Point(this.x, this.y), end, lef ? lc1 : rc1, lef ? lc2 : rc2, 1.5);
    this.setMovePath(path);

    //直线冲撞阶段,蜜蜂对任意英雄发起冲撞
    this.onMoveComplete = function () {
      let event = {
        cancelCollide: false, //是否取消接下来的冲撞动作
      };
      this.attackStageOneOver(event);
      //如果接下来的冲撞攻击行为没有被取消
      if (event.cancelCollide) return;
      var start = new Point(this.x, this.y);
      //从英雄机群中获取随机目标
      var hero = heros.getRandElement();
      //计算接下来的冲撞轨迹并添加到移动坐标集中
      var nextpath = Tools.calcLinePoints(start.x, start.y, hero ? hero.x : cavW / 2, hero ? hero.y : cavH + 100, 6, this.height, true);
      this.setMovePath(nextpath);
      this.onMoveComplete = this.returnOriginalLocation;
    };
  }

  /**延迟一定时间后向英雄发射随机数量的子弹 */
  fireBull() {
    let bct = Tools.getRandInt(2, 4);
    let tis = this;
    setTimeout(function () {
      var itv = setInterval(function () {
        var hero = heros.getRandElement();
        if (!hero || hero.isDispose || tis.isDispose) {
          clearInterval(itv);
          return;
        }
        var bull = tis.fireAt(new Point(hero.x + hero.width / 2, hero.y), true);
        bull.setStyle("triangle");
        bull.width = 5;
        bull.height = 30;
        bull.color = "lightgreen";
        bct--;
        if (bct <= 0) clearInterval(itv);
      }, 400);
    }, Tools.getRandInt(1200, 1800));
  }

  /**从上方重新进入原始坐标 */
  returnOriginalLocation() {
    let path = Tools.calcLinePoints(cavW / 2, -100, this.originalLocation.x, this.originalLocation.y, 3, 0);
    this.setMovePath(path);

    this.onMoveComplete = function () {
      this.isMoving = false;
      this.onMoveComplete = new Function();
    };
  }
}

/**兵峰类 */
class Foe_BingFeng extends Foe {
  constructor() {
    super();
    this.setFrames(resoures.anim_repeat_bingfeng());
    this.addEventListener("paint", this.planeBase_dopaint);
    this.attackStageOneOver = this.stageTwo;
  }
  stageTwo(e) {}
}

/**工峰类 */
class Foe_GongFeng extends Foe {
  constructor() {
    super();
    this.setFrames(resoures.anim_repeat_gongfeng());
    this.beginAttack = this.begAttack;
  }

  begAttack() {}
}

/**峰虫类 */
class Foe_FengChong extends Foe {
  constructor() {
    super();
    this.width = 20;
    this.height = 20;
    this.life = 50;
    this.setFrames(resoures.anim_repeat_fengchong());
  }
}

/**蜂王类 */
class Foe_FengWang extends Foe {
  leftGuard;
  rightGuard;
  constructor() {
    super();
    this.life = 300;
    this.width = 40;
    this.height = 40;
    this.setFrames(resoures.anim_repeat_fengwang_s1());
    this.onBehit = this.onBhit;
    this.addEventListener("paint", this.fw_dopaint);
    this.addEventListener("disposing", this.bee_disposing);
  }

  onBhit(foe, bull) {
    this.life -= bull.aggr;
    //根据血量设置动画帧
    if (this.life >= 300) {
      this.setFrames(resoures.anim_repeat_fengwang_s1());
    } else if (this.life >= 200 && this.life < 300) {
      this.setFrames(resoures.anim_repeat_fengwang_s2());
    } else {
      this.setFrames(resoures.anim_repeat_fengwang_s3());
    }
    if (this.life <= 0) {
      this.dispose();
    }
  }

  fw_dopaint(pen) {
    if (this.leftGuard && !this.leftGuard.isDispose) {
      //计算绝对原点坐标
      let o = new Point(this.x + this.width / 2, this.y + this.height / 2);
      //计算旋转前左护卫绝对坐标
      let a = new Point(this.x - this.leftGuard.width, this.y + this.height);
      var point = Tools.calcRolCoor(o, a, this.angle);
      this.leftGuard.x = point.x;
      this.leftGuard.y = point.y;
      this.leftGuard.angle = this.angle;
    }

    if (this.rightGuard && !this.rightGuard.isDispose) {
      //计算绝对原点坐标
      let o = new Point(this.x + this.width / 2, this.y + this.height / 2);
      //计算旋转前左护卫绝对坐标
      let a = new Point(this.x + this.width, this.y + this.height);
      var point = Tools.calcRolCoor(o, a, this.angle);
      this.rightGuard.x = point.x;
      this.rightGuard.y = point.y;
      this.rightGuard.angle = this.angle;
    }
  }

  bee_disposing() {
    //蜂王销毁时护卫跟随销毁
    if (this.leftGuard && !this.leftGuard.isDispose) {
      this.leftGuard.dispose();
    }
    if (this.rightGuard && !this.rightGuard.isDispose) {
      this.rightGuard.dispose();
    }
  }

  bindLeftGuard(bee) {
    //bee.roOrigin = new Point(bee.width + this.width / 2, -this.height / 2);
    this.leftGuard = bee;
  }

  bindRightGuard(bee) {
    //bee.roOrigin = new Point(-this.width / 2, -this.height / 2);
    this.rightGuard = bee;
  }
}

/**护卫峰 */
class Foe_HuWeiFeng extends Foe {
  constructor() {
    super();
    this.setFrames([resoures.img_huwei]);
    this.roOrigin = new Point(0, 0);
  }
}

/**英雄战机 */
class Hero extends PlaneBase {
  /**是否开启热点朝向跟踪 */
  spottracking = true;
  /**碰撞体队列 */
  collisionBodys = [];
  /**与碰撞体的最小间隔 */
  collisionBodySpace = 5;
  /**标识是否进入开火状态 */
  isFire = false;
  /**连续开火冷却时间 */
  fireCoolTime = 200;
  /**是否正在开火冷却状态 */
  isFireCool = false;
  /**与蜜蜂碰撞时触发 */
  onCollision = new Function();
  constructor() {
    super();
    this.Width = 40;
    this.Height = 56;
    this.speed = 4;
    this.aggr = 100;
    this.life = 300;
    this.roOrigin = new Point(this.width / 2, this.height / 2);
    // this.y = 400;
    this.setFrames([resoures.img_hero]);
    /**Canvas 右侧边界 */
    this.moveDirection = 0;
    //设置相对枪口坐标
    this.gunpointRel = new Point(this.width / 2, 0);
    //注册事件
    this.addEventListener("paint", this.hero_doPaint);
    this.addEventListener("keydown", this.keydown);
    this.addEventListener("keyup", this.keyup);
    this.addEventListener("mousedown", this.mousedown);
    this.onHitTarget = this.onHit;
    this.onBehit = this.behit;
    this.onCollision = this.collision;
  }

  hero_doPaint(pen) {
    //触发移动
    this.onMove();
    //触发开火
    this.onFire();
    //计算旋转角度
    if (this.spottracking) {
      this.angle = Tools.calcAngle(this.x + this.roOrigin.x, this.y + this.roOrigin.y, this.mouseLocation.x, this.mouseLocation.y);
    }
  }

  onMove() {
    if (this.moveDirection == 0) return;
    let sx = this.x + this.moveDirection;
    //限制移动范围
    if (sx < 5) return;
    if (sx > gameCanvas.width - this.width - 5) return;
    //检测是否碰撞到碰撞体
    for (let i = 0; i < this.collisionBodys.length; i++) {
      const elm = this.collisionBodys[i];
      if (elm.isDispose) {
        this.collisionBodys.splice(i, 1);
        i--;
        continue;
      }
      if (elm.containPoint_X(sx, this.collisionBodySpace) || elm.containPoint_X(sx + this.width, this.collisionBodySpace)) return;
    }
    //过滤后赋值
    this.x = sx;
  }

  onFire() {
    if (!this.isFire || this.isFireCool) return;
    this.isFireCool = false;
    this.fire();
    this.isFireCool = true;
    var tis = this;
    setTimeout(() => {
      tis.isFireCool = false;
    }, this.fireCoolTime);
  }

  //与敌方碰撞时触发
  collision(foe) {
    this.dispose();
  }

  //子弹命中敌方触发
  onHit(foe, bull) {}

  //被敌方子弹命中时触发
  behit(foe, bull) {
    this.dispose();
  }

  /**添加战机的碰撞体,在对象被释放后自动清除
   * @param {CanvaElement}elm
   */
  addCollisionBody(...elms) {
    this.collisionBodys.push(...elms);
  }

  /**向左移动 */
  moveToLeft() {
    this.moveDirection = -this.speed;
  }
  /**向右移动 */
  moveToRight() {
    this.moveDirection = this.speed;
  }
  /**停止移动 */
  stop() {
    this.moveDirection = 0;
  }
  keydown(e) {
    switch (e.keyCode) {
      case 65:
      case 37:
        this.moveToLeft();
        break;
      case 68:
      case 39:
        this.moveToRight();
        break;
      case 32:
        this.isFire = true;
    }
  }
  keyup(e) {
    switch (e.keyCode) {
      case 65:
      case 37:
        if (this.moveDirection < 0) this.stop();
        break;
      case 68:
      case 39:
        if (this.moveDirection > 0) this.stop();
        break;
      case 32:
        this.isFire = false;
        break;
    }
  }
  mousedown(e) {
    this.fireAt(new Point(e.offsetX, e.offsetY), true);
  }
}

/**子弹基类 */
class BulletBase extends CanvaElement {
  /**子弹样式 */
  style = "rect";
  /**子弹宿主 */
  master = new PlaneBase();
  /**命中敌方触发 */
  onHitTarget = new Function();
  /**标识该子弹是否已经命中过对象*/
  hasHit = false;
  /**指定子弹是否能够贯穿目标,若设置为true */
  perforative = false;
  /**名中过的敌方战机对象 */
  hitPlanes = [];
  /**子弹攻击力,默认为宿主攻击力 */
  aggr = 0;

  /**
   * @param {PlaneBase} master 子弹宿主
   */
  constructor(master) {
    super();
    this.master = master;
    this.aggr = master.aggr;
    this.addEventListener("paint", this.doPaint);
  }

  /**
   * 设置子弹样式
   * @param {"rect"|"circle"|"triangle"} style 子弹样式
   */
  setStyle(style) {
    this.style = style;
  }

  doPaint(pen) {
    //如果子弹已命中且不附带贯穿属性
    if (!this.hasHit || this.perforative) {
      for (let i = 0; i < painter.planes.length; i++) {
        const plane = painter.planes[i];
        if (this.hitPlanes.has(plane)) continue;
        /**如果命中的不是宿主和同组战舰*/
        if (this.master !== plane && this.master.groupName != plane.groupName) {
          //判断子弹左上角和右上角是否命中
          if (plane.containPoint(this.x, this.y, 0) || plane.containPoint(this.x + this.width, this.y)) {
            this.hitPlanes.push(plane);
            this.hasHit = true;
            //触发子弹命中事件
            this.onHitTarget(plane);
            //触发子弹宿主的命中回调
            this.master.onHitTarget.call(this.master, plane, this);
            //触发被命中方的被命中回调
            plane.onBehit.call(plane, this.master, this);
            //不能贯穿则释放对象
            if (!this.perforative) this.dispose();
          }
        }
      }
    }
  }
}

/**基础子弹类,子弹只能沿着Y轴飞行 */
class YBullet extends BulletBase {
  direction = "top";
  constructor(master) {
    super(master);
    this.master = master;
    //获取弹道坐标集合
    this.speed = 15;
    this.width = 3;
    this.height = 30;
    this.color = "cyan";
    this.x = master.gunpointAbs.x - this.width / 2;
    this.y = master.gunpointAbs.y - this.height;
    this.addEventListener("paint", this.beforPainting);
    this.angle = 0;
  }

  /**
   * @param {CanvasRenderingContext2D} pen
   * @returns
   */
  beforPainting(pen) {
    //子弹超出屏幕范围则销毁
    if (this.y <= -this.height || this.y >= gameCanvas.height) {
      this.dispose();
      return;
    }
    this.y += this.direction == "top" ? -this.speed : this.speed;
    switch (this.style) {
      case "rect":
        this.fillRect(pen);
        break;
      case "circle":
        this.fillCircle(pen);
        break;
      case "triangle":
        this.fillTriangle(pen);
        break;
    }
  }
}

/**定向子弹类,子弹向目标坐标点飞行 */
class LocationBullet extends BulletBase {
  space = 10;
  ext = true;
  /**
   *
   * @param {PlaneBase} master
   * @param {Point} end
   */
  constructor(master, end) {
    super(master);
    this.master = master;
    this.endPoint = end;
    this.color = "yellow";
    this.width = 2;
    this.height = 60;
    //依据圆心计算弹道偏移角度
    this.angle = Tools.calcAngle(master.gunpointAbs.x + this.roOrigin.x, master.gunpointAbs.y + this.roOrigin.y, end.x, end.y);
    //获取弹道
    this.movePoins = Tools.calcLinePoints(master.gunpointAbs.x, master.gunpointAbs.y, end.x, end.y, this.space, Math.max(this.width, this.height), this.ext);
    this.addEventListener("paint", this.beforPainting);
    //删除部分坐标使其子弹从枪口外开始绘制
    this.movePoins.splice(0, Math.floor(this.height / this.space));
    this.x = this.movePoins[0].x;
    this.y = this.movePoins[0].y;
    this.setStyle("rect");
  }
  beforPainting(pen) {
    if (this.movePoins.length <= 0) {
      this.dispose();
      return;
    }
    let point = this.movePoins.splice(0, 1)[0];
    this.x = point.x;
    this.y = point.y;
    switch (this.style) {
      case "rect":
        this.fillRect(pen);
        break;
      case "circle":
        this.fillCircle(pen);
        break;
      case "triangle":
        this.fillTriangle(pen);
        break;
    }
  }
}
